package com.doctcloud.hisinfo.common.config;

/**
 * @ClassName MybatisPlusExecutorInterceptor
 * @Author samula
 * @Descripe product by com.doctcloud
 * @Data 2025-10-07 18:38
 **/
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;

/**
 * MyBatis 字符集转换拦截器
 * 将从数据库读取的、以错误编码（如GBK）存储的字符串，在映射到实体前转换为正确的UTF-8编码。
 */
@Intercepts({
        @Signature(
                type = ResultSetHandler.class,
                method = "handleResultSets",
                args = {Statement.class}
        )
})
@Component
@Slf4j
public class CharsetConversionInterceptor implements Interceptor {

    // 数据库中实际的错误编码
    private static final Charset SOURCE_CHARSET = Charset.forName("GBK");
    // 你期望的目标编码
    private static final Charset TARGET_CHARSET = Charset.forName("UTF-8");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("【CharsetConversionInterceptor】开始执行字符集转换...");
        // 1. 先执行原始的结果集处理逻辑，得到映射后的Java对象列表
        // invocation.proceed() 的返回值就是查询结果，通常是 List<Object>
        Object result = invocation.proceed();

        // 2. 如果结果是一个列表，遍历列表中的每个对象
        if (result instanceof List<?>) {
            log.info("【CharsetConversionInterceptor】发现结果是List，准备进行转换。列表大小: {}", ((List<?>) result).size());
            List<?> resultList = (List<?>) result;
            for (Object obj : resultList) {
                // 3. 对列表中的每个对象进行字符集转换
                convertObjectCharset(obj);
            }
            log.info("【CharsetConversionInterceptor】List转换完成。");
        } else if (result != null) {
            log.info("【CharsetConversionInterceptor】发现结果是单个对象，准备进行转换。");
            // 如果查询结果不是列表（例如，查询单个对象），直接转换
            convertObjectCharset(result);
            log.info("【CharsetConversionInterceptor】单个对象转换完成。");
        } else {
            log.info("【CharsetConversionInterceptor】结果为null，无需转换。");
        }

        // 4. 返回修改后的结果
        return result;
    }

    /**
     * 递归地遍历对象的所有字符串字段，并进行编码转换
     * @param obj 要处理的对象
     * @throws IllegalAccessException
     */
    private void convertObjectCharset(Object obj) throws IllegalAccessException {
        if (obj == null) {
            return;
        }

        // 获取对象的所有声明字段（包括私有字段）
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 如果字段是字符串类型
            if (field.getType().equals(String.class)) {
                field.setAccessible(true); // 设置为可访问，以便修改私有字段
                String originalValue = (String) field.get(obj);

                if (originalValue != null) {
                    // 核心逻辑：将字符串从错误编码重新解码再编码
                    // 原理：假设数据库中的字节是 GBK 编码，但被错误地当作 ASCII/UTF-8 解码成了乱码字符串。
                    // 我们先将这个乱码字符串按错误的方式（默认UTF-8）getBytes()，得到原始的字节数组。
                    // 然后再用正确的编码（GBK）来 new String()，得到正确的字符串。
                    // 最后，如果需要，可以再 getBytes("UTF-8") 确保是 UTF-8 编码的字节。
                    // 但在Java内存中，字符串的内部表示是UTF-16，我们关心的是逻辑上的正确性。


//                    byte[] bytes = originalValue.getBytes(TARGET_CHARSET); // 反向操作，得到原始错误字节
//                    String convertedValue = new String(bytes, SOURCE_CHARSET); // 用正确的编码解析
                    String convertedValue = originalValue;
                    field.set(obj, convertedValue); // 将转换后的值设置回字段
                }
            }
            // 如果字段是自定义的实体类，可以递归处理
            else if (!field.getType().isPrimitive() && !field.getType().getName().startsWith("java.")) {
                field.setAccessible(true);
                Object nestedObject = field.get(obj);
                convertObjectCharset(nestedObject);
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可以从配置文件读取源编码和目标编码，实现更灵活的配置
        // String sourceCharset = properties.getProperty("sourceCharset", "GBK");
        // String targetCharset = properties.getProperty("targetCharset", "UTF-8");
        // SOURCE_CHARSET = Charset.forName(sourceCharset);
        // TARGET_CHARSET = Charset.forName(targetCharset);
    }
}